連載 / Pe 日活用のヒント 表 1 doit 内でのファイルに対するタ里 ファイル名 Spec Top その他 呼び出すメソッド multiple-page-article() top-page() single-page-article() 結果を収めるディレクトリにコピーする必要はない。しか し、このあとの処理で画像ファイルなどをコピーする際は、 %dir に残っている内容をもとにコピーするファイルを決 める。このため、 C01. html 、 C02. html を %dir から除 かないとコピーされてしまう。一方、どのファイルが公開 用 HTML ファイルのもとになっているのかは Spec をみ ないと分からない。そこで、 %dir への参照を引数として 渡し、呼出し先で % dir 内のファイル名を適宜削除しても らう必要がある。 ディレクトリ内に Spec ファイルがなければ、そのディ レクトリに HTML ファイルがないカ寐し、あればそれら を順次 doit メソッドで処理する。 続いて、ディレクトリ内の画像ファイルやスタイルシー ト・ファイルなどをコピーする。具体的には、この時点で %dir のキーとして残っているファイル名で、拡張子を含 むものがコピー対象となる。コピー元とコピー先の最終更 新日時を調べ、コピー先のほうカ噺しければコピーしない。 ただし、—force オプションが指定されていれば、ファイル リスト 6 doit 566 sub doit { の更新日時に関係なくコピーする。 このように、 d 。 it がディレクトリに対しておこなう処理 のうち、外から分かるのは画像ファイルなどのコピーのみ である。それ以外は、ディレクトリまたは記事のファイル に対して doit を呼び出しているだけである。 ファイルの処理 doit 内でのファイルに対する処理は比較的単純で、表 1 のようにファイル名に応じて異なるメソッドを呼び出して いる。 これらのメソッドを呼び出す際は、第 1 引数にファイル の糸鰊寸パスを渡す。 multiple-page-article の場合は、第 2 引数として d 。 it メソッドから渡された引数をそのまま渡 す。第 2 引数が multiple-page-article メソッド内で具 体的にどう使われるのかは、このメソッドを解説する際に 触れる。 ☆ こまでの説明でかなり誌面を費やしてしまったので、 コードの残りの部分は次回に説明することにしよう。 gen- htpg は機能が豊富とはいえないが、そのぶん短いので遊ん でみるのにはよいのではないだろうか。 ( いまづ・ひでよモルガン・スタンレー証券 ) 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 my ($self , $path, $pdircont) my $verbose $self->{verbose} ; my $force $self->{force} ; my $srcdir = $self—>{srcdir}; my $dstdir = $self->{dstdir}; my $relpath = $path eq $srcdir ? ”” substr ($path, die "doit : $path is outside source directory\n" substr($path, 0 , length($srcdir) ) eq $srcdir or length($srcdir) + 1 ) ; if ( -d $path ) { my $dh = IO: :Dir->new($path) or die "opendir: $path: $ ! \n" my @dir = $dh—>read() ; $dh->close() ; splice(@dir, 0 , 2 ) ; my %dir; @dir{@dir} ( 1 ) x @dir; my $tgtdir = $dstdir; $tgtdir "/$relpath" if ( $relpath ne -p" , $tgtdir) unless ( -d $tgtdir ) ; system("mkdir" for my $i ( sort @dir ) { delete $dir{$i}; UN 工 X MAGAZINE 2005. 10 89
連載 / Pe 日活用のヒント リスト 5 main 663 sub main { my $self shift; 664 if ( $self->{dumpcontdb} ) { 665 $self—>dumpcontdb() ; 666 667 else { 668 if ( @ARGV 669 $self—>{recursive} 670 $self—>doit($self—>{srcdir}) ; 671 672 else { 673 for my $i ( @ARGV ) { 674 $self—>doit (normalize-path($i) ) ; 675 676 677 678 679 } るコマンド行オプションの処理は終っており、 @ARGV に 前から 2 / 3 程度までがディレクトリ処理で、残りの 1 / 3 入っているのはオプションではないコマンド行引数である。 程度がファイルの処理である。 それらがファイルあるいはディレクトリとして処理対象に ディレクトリの処理 なるのである。 なんらかのオプションの値としてファイルやディレクト ディレクトリの処理では、まずディレクトリの内容を リを指定する場合や、オプションではなく処理対象として @dir に読み込む。続いて@dir の全要素をキーとしても ファイルやディレクトリを指定する場合でも、相対パスは つ %dir を作成する。ディレクトリ内のファイルまたはデ つねにカレント・ディレクトリからのパスとなる。制作用 ィレクトリを処理するたびに、 % dir からファイル名 / ディ ディレクトリからの相対パスと解釈されることはない。 レクトリ名を削除していく。 main カ鮃び出された時点で@ARGV カ啌の場合は、制 ディレクトリの内容を読み込むと、まずそのなかのサ 作用ディレクトリの下全体が処理対象となるように、 -re- プディレクトリに対する処理をおこなう。 -recursive オ cursive オプションが指定された大態にしたうえで、引数 プションが指定されていれば、サプディレクトリに対して として制作用ディレクトリを渡して doit メソッドを呼び doit メソッドを再帰的に呼び出す。つまり、深さ優先の 出す。 再帰呼出しをおこなっている。なぜ深さ優先かというと、 doit メソッドにユーザーが入力したファイル名あるいは トップページが最後に処理されなければならないからだ。 ディレクトリ名を渡す際は、さきほど述べた絶対パスを使 -recursive オプションカ甘日疋されていない場合、サプディ 用する方針に従い、 normalize-path() サプルーチンで絶 レクトリは単純に % dir から除かれるだけである 続 0 、、、 spec , イ、・があるかどうかを調ミ、あれば 対パスに変換する。 そのファイルに対して doit メソッドを呼び出す。ただし、 doit その場合は % dir への参照を第 2 引数として渡す。これが doit メソッドの呼出しで第 2 引数が渡される唯一のケース genhtpg の中核となるメソッドである doit を末尾のリ である。 スト 6 に示す。これは、 こで %dir への参照を渡す理 やや込み入っているが、 $obj—>doit( パス名 , ハッシュへの参照 ) 由を説明しておこう。これは、呼出し先で % dir の内容を 適宜削除してもらうためである。たとえば、 sample ディ というかたちで呼び出す。パス名としては、制作用ディ レクトリについて考えると、 long-article ディレクトリの レクトリ内のファイルまたはディレクトリに対応するもの を絶対パスで渡す。ハッシュへの参照は通常は渡されない 下の C01. html 、 C02. html は生成結果の HTML ファ イルのもとになっているだけで、これらのファイルを生成 が、それについては彳もする。 88 UNIX MAGAZINE 2005 . 10
連載 / Pe 日活用のヒント を実行したうえで、残りのコマンド行オプションを読み リスト 4 DESTROY 234 sub DESTROY { 込むために GetOptions() を実行する。 my $self shift ; 235 if ( ref $self & & $self—>{contdb} ) { 236 これで処理オプションの設定は終りで、続いてテンプ untie %{$self->{contdb}}; 237 238 レートを選ぶためのデータを template ディレクトリの 239 } Switch ファイルから読み込む。 残りは目次データの参照 / 更新の準備である。目次デー スカ皸棄されるときに自動的に呼び出されるメソッドで、 タは、サイト制作用ディレクトリの直下にある Berkeley オプジェクト指向プログラミングでは一般にデストラクタ DB ファイル Cont. db に保存されている。目次データは と呼ばれる。処理の流れには沿っていないが、 new の次に 己事ごとにある。複数のページから構成される記事もある 説明しておくのカ随当だろう。 のでページごとではない。 1 つ 1 つの目次データはハッシ デストラクタが不要なクラスも多いが、 GenHTML- ュ形式で、それを Berkeley DB ファイルに収めるために Article クラスには必要である。なぜなら、 contdb 属性 MLDBM を使用している。 MLDBM の詳細はドキュメ に保存されている値に対して untie を実行しないと、目次 ントを参照してもらうことにして、 こでは以下の点を理 データの作成 / 更新がファイルに保存されないことがある 解しておけば十分だろう。 からである。処理効率向上のために Berkeley DB ファイ genhtpg の先頭部分に以下の謎杢がある。 ルと結びつけられたハッシュに対する代入はキャッシュさ れ、適当なタイミングでファイルへの書込みカ起こる。 un- use MLDBM qw(DB_Fi1e Storab1e) ; tie がおこなわれないままプログラムが終了すると、キャッ MLDBM モジュールの使用をこのように宣言すること シュに入っただけでファイルに保存されていないデータが で、以下の事柄を指定している。 生じる可能性がある。 ・ファイル形式として Berkeley DB を使用する。 じつはごく最近までデストラクタの必要性に気づいてい ・配列やハッシュをシリアライズして保存し、デシリアラ なかった。ある記事の内容を更新して公開用 HTML ファ イズして読み出すためのモジュールとして Storable を イルを再生成したあと、さらにトップページを生成したと 使用する。 ころ、トッフ。ページの目次にある記事の最終更新日が書き 換えられていなかった。 dumpcontdb. pl で目次データを そして、 new のなかでほば以下のような内容の処理をお みると、その記事の最終更新日が以前のままになっていた。 こなっている。 この現象から untie がおこなわれていないことに気づき、 tie %contdb, "MLDBM" "Cont . db", , デストラクタを導入したのである。その結果、この問題は O_CREAT IO_RDWR, 0644 ; 解消された。この過程で、 dumpcontdb. pl カ虫立したプ %contdb への読み書きは、実際には以下のような動作 ログラムであるよりも、 genhtpg が同様の機能を備えてい につながる。 るほうが便利だと感じ、 -dumpcontdb オプションを導入 ・・ } への代入は、値をシリアライズして ・ $contdb{ ・ 0 Cont. db に書き込むことになる。 ・・ } の参照は、 Cont. db から読み込んでデ ・ $contdb{ ・ シリアライズした結果を返すことになる。 さらに、作成したインスタンスの contdb 属性として、 %contdb への参照を保存している。 DESTROY DESTROY ( リスト 4 ) は perl でクラスのインスタン 1 蝨 al Ⅱ new の次に実行される main をリスト 5 に小す。 main の働きは次の 2 つである。ます、 -dumpcontdb オプシ ョンが指定されていたら dumpcontdb メソッドを呼び出 す。そうでない場合は、コマンド行で指定されたファイル やディレクトリについて、順次 doit メソッドを呼び出す。 main カ剛乎び出された時点では、すでに GetOptions によ 87 UNIX MAGAZINE 2005. 10